﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;

namespace System
{
	public static class EnumerationExtensions
	{
		public static string DisplayName(this Enum source)
		{
			var field = default(FieldInfo);
			var values = Enum.GetValues(source.GetType());
			var ret = string.Empty;

			var isFlag = source.GetType().GetCustomAttribute<FlagsAttribute>() != null;

			if (isFlag == false)
			{
				var name = Enum.GetName(source.GetType(), source);

				if (string.IsNullOrEmpty(name) == false)
				{
					field = source.GetType().GetField(name);
					var attr = field.GetCustomAttribute<EnumMemberAttribute>();

					if (attr != null)
					{
						return attr.Value;
					}
				}
			}
			else
			{
				foreach (var value in values)
				{
					if (source.HasFlag((Enum)value))
					{
						field = source.GetType().GetField(value.ToString());
						var attr = field.GetCustomAttribute<EnumMemberAttribute>();

						if (attr != null && string.IsNullOrEmpty(attr.Value) == false)
						{
							ret += string.IsNullOrEmpty(ret) ? attr.Value : ", " + attr.Value;
						}
					}
				}
			}

			return ret;
		}
	}

	public static class AttributeExtensions
	{
		private static IEnumerable<TAttr> GetCustomAttribute<TAttr, TItem>(TItem item, Func<TItem, bool, object[]> getter, bool inherit = true)
			where TAttr : Attribute
			where TItem : class
		{
			var attrs = getter(item, inherit);

			if (attrs != null && attrs.Length != 0)
			{
				if (attrs.Length == 1)
				{
					return attrs.Cast<TAttr>();
				}
				else 
				{
					throw new InvalidOperationException("Attribute usage allows multiple instances. Use GetCustomAttributes<> method instead");
				}
			}
			else
			{
				return Enumerable.Empty<TAttr>();
			}
		}

		public static T GetCustomAttribute<T>(this FieldInfo field, bool inherit = true) where T : Attribute
		{
			return GetCustomAttribute<T, FieldInfo>(field, (f, b) => f.GetCustomAttributes(b), inherit).SingleOrDefault();
		}

		public static IEnumerable<T> GetCustomAttributes<T>(this FieldInfo field, bool inherit = true) where T : Attribute
		{
			return GetCustomAttribute<T, FieldInfo>(field, (f, b) => f.GetCustomAttributes(b), inherit);
		}

		public static T GetCustomAttribute<T>(this Type type, bool inherit = true) where T : Attribute
		{
			return GetCustomAttribute<T, Type>(type, (t, b) => t.GetCustomAttributes(b), inherit).SingleOrDefault();
		}

		public static IEnumerable<T> GetCustomAttributes<T>(this Type type, bool inherit = true) where T : Attribute
		{
			return GetCustomAttribute<T, Type>(type, (t, b) => t.GetCustomAttributes(b), inherit);
		}
	}
}
